package cn.zwx.common.redisson.aspect;

import cn.zwx.base.enums.ResponseCodeEnum;
import cn.zwx.base.exception.BizWrapException;
import cn.zwx.common.redisson.annotation.RedissonLock;
import cn.zwx.common.redisson.enums.LockModelEnum;
import cn.zwx.common.redisson.utils.LockUtil;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * @author zhangwenxue
 */
@Aspect
public class LockAspect {

    private final static Logger logger = LoggerFactory.getLogger(LockAspect.class);

    @Resource
    private LockUtil lockUtil;

    @Pointcut("@annotation(lock)")
    public void controllerAspect(RedissonLock lock) {
    }


    @Around(value = "controllerAspect(lock)", argNames = "proceedingJoinPoint,lock")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint, RedissonLock lock) throws BizWrapException {
        String[] keys = lock.keys();
        if (keys.length == 0) {
            throw new BizWrapException(ResponseCodeEnum.REDISSON_LOCK_KEY_ERROR);
        }
        // 等待锁定时间
        long attemptTimeout = lock.attemptTimeout();
        // 锁定最长时间
        long lockWatchdogTimeout = lock.lockWatchdogTimeout();
        // 获取锁类型 如果是自动则需要做判断
        LockModelEnum lockModel = LockUtil.getAutoLockModel(lock, keys);
        if (!lockModel.equals(LockModelEnum.MULTIPLE) && !lockModel.equals(LockModelEnum.RED_LOCK) && keys.length > 1) {
            throw new RuntimeException("参数有多个,锁模式为->" + lockModel.name() + ".无法锁定");
        }
        logger.info("锁模式->{},等待锁定时间->{}秒.锁定最长时间->{}秒",lockModel.name(),attemptTimeout/1000,lockWatchdogTimeout/1000);
        // 获取锁对象
        RLock rLock = lockUtil.getLock(proceedingJoinPoint,lock,lockModel);
        // 如果没有获取到锁对象 直接执行方法
        if (null == rLock){
            try {
                return proceedingJoinPoint.proceed();
            } catch (Throwable e) {
                logger.error("LockAspect#aroundAdvice:{}",proceedingJoinPoint.getSignature().getName(),e);
               throw processThrowable(e);
            }
        }
        // 否则执行加锁逻辑
        try {
            if (attemptTimeout == -1) {
                //一直等待加锁
                rLock.lock(lockWatchdogTimeout, TimeUnit.MILLISECONDS);
            } else {
                boolean tryLock = rLock.tryLock(attemptTimeout, lockWatchdogTimeout, TimeUnit.MILLISECONDS);
                if (!tryLock){
                    throw new BizWrapException(ResponseCodeEnum.REDISSON_TRY_LOCK_ERROR);
                }
            }
            // 执行aop方法
            return proceedingJoinPoint.proceed();
        }catch (Throwable e){
            logger.error("LockAspect#aroundAdvice:{}",proceedingJoinPoint.getSignature().getName(),e);
            throw processThrowable(e);
        }finally {
            LockUtil.processUnlock(rLock);
        }
    }

    private static BizWrapException processThrowable(Throwable e)  {
        if (e instanceof  BizWrapException){
           return  (BizWrapException) e;
        }
        final String errorMsg = StringUtils.isBlank(e.getMessage()) ? ResponseCodeEnum.UNKNOWN_ERROR.getMsg() : e.getMessage();
        return new BizWrapException(ResponseCodeEnum.UNKNOWN_ERROR.getCode(),errorMsg );
    }


}
